5 复合

说明:建立类之间的关系除了继承还有复合

5.1 什么是复合

说明:使用复合可组合多个对象,让它们分工协作
原理:0-C 中,复合是通过包含作为实例变量的对象指针实现的。

5.2 自定义NSLog()

说明:在类中提供description方法就可以自定义NSLog()会如何输出对象。

在init中初始化实例属性

说明:使用new创建新对象时后台进行了两步

  1. 为对象分配内存,用来存放实例变量(如果实例属性是对象,被初始化为nil
  2. 自动调用init方法,之后对象进入可用状态

扩展:init方法中使用if (self = [super init])是为了完成父类的初始化并确保父类返回的对象和一开始创建的对象是一致的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#import <Foundation/Foundation.h>

// --------------------------------------------------

@interface Tire : NSObject
@end // Tire


@implementation Tire

- (NSString *) description
{
return (@"I am a tire. I last a while");
} // description

@end // Tire



// --------------------------------------------------

@interface Engine : NSObject
@end // Engine


@implementation Engine

- (NSString *) description
{
return (@"I am an engine. Vrooom!");
} // description

@end // Engine


// --------------------------------------------------

@interface Car : NSObject
{
Engine *engine;
Tire *tires[4];
}

- (void) print;

@end // Car


@implementation Car

- (id) init
{
if (self = [super init]) {
engine = [Engine new];

tires[0] = [Tire new];
tires[1] = [Tire new];
tires[2] = [Tire new];
tires[3] = [Tire new];
}

return (self);

} // init

- (void) print
{
NSLog (@"%@", engine);

NSLog (@"%@", tires[0]);
NSLog (@"%@", tires[1]);
NSLog (@"%@", tires[2]);
NSLog (@"%@", tires[3]);

} // print

@end // Car


// --------------------------------------------------

int main (int argc, const char * argv[])
{
Car *car;

car = [Car new];
[car print];

return (0);

} // main

5.3 存取方法

说明:是用来读取或改变某个对象属性的方法。

存取方法 方法命名(Cocoa惯例) 用途
getter getPropertyName 返回实例变量的值
setter propertyName 将实例变量的值赋为参数所指向的值

建议:如果要对对象中的属性进行操作,因该尽量使用对象提供的存取方法,而不是直接改变对象里面的值。
注意:不要将get作为getter方法的前缀,因为get出现在Cocoa的方法名称中意味着这个方法会将你传递的参数作为指针来返回数值。

5.3.1 设置 engine 属性的存取方法

说明:@interface@implementation中同时增加存取方法。
扩展:在内存管理和对象的多有权方面Engingetter方法和setter方法还存在着问题。

5.3.2 设置 tires 属性的存取方法

说明:不仅要知道新轮胎,还要知道新轮胎的位置。
注意:防御式编程
对无效索引进行防御。

5.3.3 Car 类代码的其他变化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#import <Foundation/Foundation.h>

// --------------------------------------------------

@interface Tire : NSObject
@end // Tire


@implementation Tire

- (NSString *) description
{
return (@"I am a tire. I last a while");
} // description

@end // Tire



// --------------------------------------------------

@interface Engine : NSObject
@end // Engine


@implementation Engine

- (NSString *) description
{
return (@"I am an engine. Vrooom!");
} // description

@end // Engine


// --------------------------------------------------

@interface Car : NSObject
{
Engine *engine;
Tire *tires[4];
}

- (Engine *) engine;

- (void) setEngine: (Engine *) newEngine;

- (Tire *) tireAtIndex: (int) index;

- (void) setTire: (Tire *) tire
atIndex: (int) index;

- (void) print;

@end // Car


@implementation Car

- (id) init
{
if (self = [super init]) {
engine = [Engine new];

tires[0] = [Tire new];
tires[1] = [Tire new];
tires[2] = [Tire new];
tires[3] = [Tire new];
}

return (self);

} // init


- (Engine *) engine
{
return (engine);
} // engine


- (void) setEngine: (Engine *) newEngine
{
engine = newEngine;
} // setEngine


- (void) setTire: (Tire *) tire
atIndex: (int) index
{
if (index < 0 || index > 3) {
NSLog (@"bad index (%d) in setTire:atIndex:",
index);
exit (1);
}

tires[index] = tire;

} // setTire:atIndex:


- (Tire *) tireAtIndex: (int) index
{
if (index < 0 || index > 3) {
NSLog (@"bad index (%d) in tireAtIndex:",
index);
exit (1);
}

return (tires[index]);

} // tireAtIndex:



- (void) print
{
NSLog (@"%@", engine);

NSLog (@"%@", tires[0]);
NSLog (@"%@", tires[1]);
NSLog (@"%@", tires[2]);
NSLog (@"%@", tires[3]);

} // print

@end // Car


// --------------------------------------------------

int main (int argc, const char * argv[])
{
Car *car = [Car new];

Engine *engine = [Engine new];
[car setEngine: engine];

for (int i = 0; i < 4; i++) {
Tire *tire = [Tire new];

[car setTire: tire atIndex: i];
}

[car print];

return (0);

} // main

5.4 扩展 CarParts 程序

说明:结合继承复合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
#import <Foundation/Foundation.h>

// --------------------------------------------------

@interface Tire : NSObject
@end // Tire


@implementation Tire

- (NSString *) description
{
return (@"I am a tire. I last a while");
} // description

@end // Tire


// --------------------------------------------------
// 新型轮胎
@interface AllWeatherRadial : Tire
@end // AllWeatherRadial


@implementation AllWeatherRadial

- (NSString *) description
{
return (@"I am a tire for rain or shine.");
} // description

@end // AllWeatherRadial


// --------------------------------------------------

@interface Engine : NSObject
@end // Engine


@implementation Engine

- (NSString *) description
{
return (@"I am an engine. Vrooom!");
} // description

@end // Engine


// --------------------------------------------------
// 新型引擎
@interface Slant6 : Engine
@end // Slant6


@implementation Slant6

- (NSString *) description
{
return (@"I am a slant-6. VROOOM!");
} // description

@end // Slant6


// --------------------------------------------------

@interface Car : NSObject
{
Engine *engine;
Tire *tires[4];
}

- (Engine *) engine;

- (void) setEngine: (Engine *) newEngine;

- (Tire *) tireAtIndex: (int) index;

- (void) setTire: (Tire *) tire atIndex: (int) index;

- (void) print;

@end // Car


@implementation Car

- (id) init
{
if (self = [super init]) {
engine = [Engine new];

tires[0] = [Tire new];
tires[1] = [Tire new];
tires[2] = [Tire new];
tires[3] = [Tire new];
}

return (self);

} // init


- (Engine *) engine
{
return (engine);
} // engine


- (void) setEngine: (Engine *) newEngine
{
engine = newEngine;
} // setEngine


- (void) setTire: (Tire *) tire atIndex: (int) index
{
if (index < 0 || index > 3) {
NSLog (@"bad index (%d) in setTire:atIndex:", index);
exit (1);
}

tires[index] = tire;

} // setTire:atIndex:


- (Tire *) tireAtIndex: (int) index
{
if (index < 0 || index > 3) {
NSLog (@"bad index (%d) in tireAtIndex:", index);
exit (1);
}

return (tires[index]);

} // tireAtIndex:



- (void) print
{
NSLog (@"%@", engine);

NSLog (@"%@", tires[0]);
NSLog (@"%@", tires[1]);
NSLog (@"%@", tires[2]);
NSLog (@"%@", tires[3]);

} // print

@end // Car


// --------------------------------------------------

int main (int argc, const char * argv[])
{
Car *car = [Car new];

for (int i = 0; i < 4; i++) {
Tire *tire = [AllWeatherRadial new];

[car setTire: tire
atIndex: i];
}

Engine *engine = [Slant6 new];
[car setEngine: engine];

[car print];

return (0);

} // main

5.5 复合还是继承

说明:在进行面向对象编程时通常会犯这样的错误-任何东西都想使用继承。

比较 类之间的关系
继承 is a
复合 has a

5.6 小结